package com.snailhd.framework.i18n.nacos;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

/**
 *
 * @author snailhd
 */
@Slf4j
@Configuration
public class NacosResourceBundleMessageSource extends AbstractMessageSource implements ResourceLoaderAware {

    ResourceLoader resourceLoader;

    @Resource
    NacosConfig nacosConfig;

    private boolean nacosRegisterRunning = false;

    private final static long RETRY_REGISTER_INTERVAL = 1000 * 60 * 60 * 30;

    public final static ConcurrentHashMap<String,ConcurrentHashMap<String,String>> LOCAL_CACHE = new ConcurrentHashMap<>();

    public LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(100);

    public final static ConcurrentHashMap<String,RegisterInfo> REGISTERED = new ConcurrentHashMap<>();


    @PostConstruct
    public void init() throws InterruptedException {
        List<String> superLocales = nacosConfig.getSuperLocales();
        for (String locale: superLocales){
            queue.put(locale);
        }
        createNacosRegsiter();
    }

    void createNacosRegsiter(){
        if(!nacosRegisterRunning){
            nacosRegisterRunning = true;
            new RegisterNacos( nacosConfig,queue,nacosRegisterRunning).run();
        }
    }


    @Override
    public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
        this.resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader);
    }

    /**
     * 从缓存中取出国际化配置对应的数据 或者从父级获取
     *
     * @param code code
     */
    public String getSourceFromCache(String code, Locale locale) {
        String language = locale.toString();
        Map<String, String> props = LOCAL_CACHE.get(language);
        if (null != props && props.containsKey(code)) {
            return props.get(code);
        } else {

            /*
             * 获取nacos国际化文件注册信息，防止重复注册
             */
            RegisterInfo registerInfo = REGISTERED.get(language);
            if(registerInfo==null){
                REGISTERED.putIfAbsent(language,new RegisterInfo(System.currentTimeMillis()));
            }else{
                if(registerInfo.getLastExecuteTime()+RETRY_REGISTER_INTERVAL<System.currentTimeMillis()){
                    REGISTERED.put(language,new RegisterInfo(System.currentTimeMillis()));
                    try {
                        queue.put(language);
                        createNacosRegsiter();
                    } catch (InterruptedException e) {
                        log.error("注册监听nacos国际化文件队列异常：{}",language,e);
                    }
                }
            }

            try {
                if (null != this.getParentMessageSource()) {
                    return this.getParentMessageSource().getMessage(code, null, locale);
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
            return code;
        }
    }

    @Override
    @Nullable
    protected MessageFormat resolveCode(String code, Locale locale) {
        String msg = getSourceFromCache(code, locale);
        return  new MessageFormat(msg, locale);
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return getSourceFromCache(code, locale);
    }

    @Setter
    @Getter
    @NoArgsConstructor
    static class RegisterInfo implements Serializable {
        private Long lastExecuteTime;

        public RegisterInfo(Long lastExecuteTime) {
            this.lastExecuteTime = lastExecuteTime;
        }
    }



}
